﻿using System;
using System.Collections.Generic;
using System.Timers;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using FlanProject.Combats.Actions;
using FlanProjectData.GameObjects;
using FlanProjectData.Skills;
using FlanProjectData.Sprites;
using FlanProjectData.Managers;

namespace FlanProject.Combats
{
    public abstract class CombatCharacter
    {
        #region Properties

        #region Directions

        protected const int MOVE_UP = -1;
        protected const int MOVE_DOWN = 1;
        protected const int MOVE_LEFT = -1;
        protected const int MOVE_RIGHT = 1;

        #endregion

        #region Character Movements

        [ContentSerializerIgnore]
        public Vector2 OriginalPosition { get; set; }
        [ContentSerializerIgnore]
        public Vector2 PreviousPosition { get; set; }
        [ContentSerializerIgnore]
        public Vector2 Position { get; set; }
        [ContentSerializerIgnore]
        public Vector2 Direction { get; set; }

        #endregion

        #region Character Data

        public int PlayerIndex { get; set; }
        public String Name { get; set; }
        public CharacterStat BaseStats { get; set; }
        public CharacterStat Stats { get; set; }

        [ContentSerializerIgnore]
        public ArtificialIntelligence ArtificialIntelligence { get; set; }

        #endregion

        #region Character Skills

        [ContentSerializerIgnore]
        public List<Skill> SkillList { get; set; }
        public List<String> Skills { get; set; }

        #endregion

        #region Character Texture Data

        [ContentSerializerIgnore]
        public Color[] TextureData { get; set; }

        #endregion

        #region Character States

        public enum States { Idle, Walking, Running, Attacking, Defending, Spellcasting, EndSpellcasting, Hit, Dying, Dead, Victory }

        [ContentSerializerIgnore]
        public States CurrentState { get; set; }
        public bool IsDeadOrDying { get { return CurrentState == States.Dead || CurrentState == States.Dying; } }

        #endregion

        #region Character Animations

        public Animation IdleAnimation { get; set; }
        public Animation WalkAnimation { get; set; }
        public Animation AttackAnimation { get; set; }
        public Animation OnHitAnimation { get; set; }
        public Animation DeathAnimation { get; set; }
        public Animation VictoryAnimation { get; set; }

        // Optional animations
        [ContentSerializer(Optional = true)]
        public Animation RunAnimation { get; set; }
        [ContentSerializer(Optional = true)]
        public Animation SpellcastAnimation { get; set; }
        [ContentSerializer(Optional = true)]
        public Animation EndSpellcastAnimation { get; set; }
        [ContentSerializer(Optional = true)]
        public Animation DefenseAnimation { get; set; }

        // Only one animation at a time
        [ContentSerializerIgnore]
        public AnimatingSprite CurrentAnimation { get;  set; }

        [ContentSerializer(Optional = true)]
        public int OnStartLookDirection { get; set; }
        [ContentSerializerIgnore]
        public SpriteEffects Flip { get; set; }
        [ContentSerializer(Optional = true)]
        public bool IsFloating { get; set; }
        [ContentSerializer(Optional = true)]
        public int HeightOffset { get; set; }

        #endregion

        #region Artificial Intelligence

        [ContentSerializerIgnore]
        public CombatAction Action { get; set; }

        #endregion

        #endregion

        // Combat character constructor
        public CombatCharacter()
        {
            Direction = Vector2.Zero;
            Flip = SpriteEffects.None;

            CurrentAnimation = new AnimatingSprite();
            SkillList = new List<Skill>();
            Skills = new List<String>();
            TextureData = new Color[0];
        }

        // Load character content
        public virtual void LoadContent()
        {
            // Adapt to spritesheet
            if (OnStartLookDirection == MOVE_LEFT)
                Flip = SpriteEffects.FlipHorizontally;
        }

        // Update character
        public virtual void Update(GameTime gameTime)
        {
            UpdateAnimation();

            if (CurrentState != States.Dead)
            {
                if (Action != null && CurrentState != States.Hit)
                {
                    // Update the combat action
                    Action.Update(gameTime);

                    // Idle after each action
                    if (Action.Stage == CombatAction.CombatActionStage.Complete)
                        Action = (Action is IdleCombatAction) ? null : new IdleCombatAction(this);
                }
            }
        }

        // Draw current animation
        public virtual void Draw(SpriteBatch spriteBatch, GameTime gameTime)
        {
            // Flip the sprite to face the way we are moving
            if (Direction.X > 0)
            {
                if (OnStartLookDirection == MOVE_LEFT)
                    Flip = SpriteEffects.FlipHorizontally;
                else
                    Flip = SpriteEffects.None;
            }
            else if (Direction.X < 0)
            {
                if (OnStartLookDirection == MOVE_LEFT)
                    Flip = SpriteEffects.None;
                else
                    Flip = SpriteEffects.FlipHorizontally;
            }

            CurrentAnimation.Draw(spriteBatch, Flip, Position, gameTime);
        }

        // Update current animation depending on character state
        public void UpdateAnimation()
        {
            if (CurrentAnimation == null)
                throw new NullReferenceException("No animation loaded.");

            // Only one animation at a time, depending on the character's current state
            switch (CurrentState)
            {
                case States.Idle:
                    CurrentAnimation.PlayAnimation(IdleAnimation);
                    break;

                case States.Attacking:
                    CurrentAnimation.PlayAnimation(AttackAnimation);
                    break;

                case States.Defending:
                    CurrentAnimation.PlayAnimation(DefenseAnimation);
                    break;

                case States.Spellcasting:
                    CurrentAnimation.PlayAnimation(SpellcastAnimation);
                    break;

                case States.EndSpellcasting:
                    CurrentAnimation.PlayAnimation(EndSpellcastAnimation);
                    break;

                case States.Walking:
                    CurrentAnimation.PlayAnimation(WalkAnimation);
                    break;

                case States.Running:
                    CurrentAnimation.PlayAnimation(RunAnimation);
                    break;

                case States.Hit:
                    CurrentAnimation.PlayAnimation(OnHitAnimation);
                    break;

                case States.Dead:
                    CurrentAnimation.PlayAnimation(DeathAnimation);
                    break;

                case States.Victory:
                    CurrentAnimation.PlayAnimation(VictoryAnimation);
                    break;
            }
        }

        // On hit invincibility after getting hit
        public void OnHitInvincibility_Elapsed(object sender, ElapsedEventArgs e)
        {
            CurrentState = States.Idle;
        }

        // Receive damage from enemy
        public int ReceiveDamage(CombatCharacter enemy)
        {
            if (CurrentState != States.Defending)
            {
                if (CurrentState != States.Dead && CurrentState != States.Hit)
                {
                    Stats.CurrentHealthPoints -= enemy.Stats.Strength;

                    if (Stats.CurrentHealthPoints > 0)
                    {
                        CurrentState = States.Hit;

                        // On hit invincibility
                        var onHitInvincibility = new Timer() { Interval = 500, AutoReset = false };
                        onHitInvincibility.Elapsed += new ElapsedEventHandler(OnHitInvincibility_Elapsed);
                        onHitInvincibility.Start();
                    }
                    else
                    {
                        Stats.CurrentHealthPoints = 0;
                        CurrentState = States.Dead;
                    }

                    return enemy.Stats.Strength;
                }
            }

            return 0;
        }

        // Handle user states
        public virtual void HandleUserStates()
        {
            // Removing offset after death
            if (CurrentState == States.Dead && IsFloating)
            {
                Position = new Vector2(Position.X, Position.Y + HeightOffset);
                IsFloating = false;
            }

            //if (InputManager.CurrentKeyboardState.GetPressedKeys().Length == 0 && CurrentState == States.Defending)
            //{
            //    CurrentState = States.Idle;
            //}

            if (CurrentState == States.Dead || CurrentState == States.Hit || CurrentState == States.Spellcasting
                || CurrentState == States.Defending || CurrentState == States.Victory)
                return;

            //if (InputManager.CurrentKeyboardState.GetPressedKeys().Length == 0
            //    && (CurrentState == States.Walking || CurrentState == States.Running))
            //{
            //    CurrentState = States.Idle;
            //}

            // Character idling when animation ends
            //if (CurrentState != States.Idle && CurrentState != States.Defending && CurrentState != States.Walking
            //    && CurrentState != States.Running)
            //{
            //    if (CurrentAnimation.IsAnimationComplete)
            //    {
            //        CurrentState = States.Idle;
            //    }
            //}
        }

        // Face target
        public void FaceTarget(Vector2 target)
        {
            if (Position.X < target.X)
            {
                if (OnStartLookDirection == MOVE_RIGHT)
                    Flip = SpriteEffects.None;
                else
                    Flip = SpriteEffects.FlipHorizontally;
            }
            else
            {
                if (OnStartLookDirection == MOVE_RIGHT)
                    Flip = SpriteEffects.FlipHorizontally;
                else
                    Flip = SpriteEffects.None;
            }
        }
    }
}
